package bma.common.langutil.xdom.core;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import bma.common.langutil.core.Function;
import bma.common.langutil.core.IteratorUtil;
import bma.common.langutil.xdom.Xdom;
import bma.common.langutil.xdom.XdomType;
import bma.common.langutil.xdom.XdomUtil;

public abstract class XdomObject {

	public abstract Map<String, Xdom> createProperties(Object value);

	public Xdom bind(Object obj) {
		Xdom r = XdomUtil.value(obj);
		if (r != null)
			return r;
		if (obj instanceof List) {
			return new XList((List) obj);
		}
		if (obj instanceof Map) {
			return new XMap((Map) obj);
		}
		return new XBObject(obj);
	}

	class XList extends Xdom {
		private List value;

		public XList(List v) {
			super();
			value = v;
		}

		@Override
		public XdomType type() {
			return XdomType.ARRAY;
		}

		@Override
		public int size() {
			return value.size();
		}

		@Override
		public Xdom get(int index) {
			if (!has(index)) {
				return XdomNULL.NULL;
			}
			return bind(value.get(index));
		}

		@SuppressWarnings("unchecked")
		@Override
		public Iterator<Xdom> arrayIterator() {
			Iterator<Object> it = value.iterator();
			return IteratorUtil.wrap(it, new Function<Object, Xdom>() {
				public Xdom apply(Object input) {
					return bind(input);
				};
			});
		}
	}

	class XMap extends Xdom {

		private Map value;

		public XMap(Map value) {
			super();
			this.value = value;
		}

		@Override
		public XdomType type() {
			return XdomType.MAP;
		}

		@Override
		public int size() {
			return value.size();
		}

		@Override
		public boolean has(String key) {
			return value.containsKey(key);
		}

		@Override
		public Xdom get(String fieldName) {
			return bind(value.get(fieldName));
		}

		@SuppressWarnings("unchecked")
		@Override
		public Iterator<String> mapNames() {
			Iterator<Object> it = value.keySet().iterator();
			return IteratorUtil.wrap(it, new Function<Object, String>() {
				@Override
				public String apply(Object input) {
					return input.toString();
				}
			});
		}
	}

	class XBObject extends Xdom {

		private Object value;
		private Map<String, Xdom> properties;

		public XBObject(Object value) {
			super();
			this.value = value;
		}

		@Override
		public XdomType type() {
			return XdomType.MAP;
		}

		@Override
		public String xtype() {
			return value.getClass().getName();
		}

		protected Map<String, Xdom> prop() {
			if (properties == null) {
				properties = createProperties(value);
			}
			return properties;
		}

		@Override
		public int size() {
			return prop().size();
		}

		@Override
		public boolean has(String fieldName) {
			return prop().containsKey(fieldName);
		}

		@Override
		public Xdom get(String fieldName) {
			return prop().get(fieldName);
		}

		@Override
		public Iterator<String> mapNames() {
			return prop().keySet().iterator();
		}
	}
}
